package com.pactera.asmp.server.job.dispatch.impl;

import com.alibaba.fastjson.JSONObject;
import com.pactera.asmp.server.common.constants.DeviceEventType;
import com.pactera.asmp.server.common.constants.TaskEventType;
import com.pactera.asmp.server.dao.EventLogMapper;
import com.pactera.asmp.server.entity.EventLog;
import com.pactera.asmp.server.entity.Subtask;
import com.pactera.asmp.server.entity.TaskDTO;
import com.pactera.asmp.server.entity.TaskDevice;
import com.pactera.asmp.server.job.dispatch.*;
import com.pactera.asmp.server.job.dispatch.impl.DeviceEvent;
import com.pactera.asmp.server.job.dispatch.impl.TaskEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Date;

@Slf4j
public abstract class BaseManager implements DeviceEventListener, TaskEventListener , ApplicationContextAware {

    private BaseDispatchBus dispatcher;

    private static ApplicationContext context;

    public BaseManager(){
        dispatcher = initDispatcher();
        baseInit();
    }

    abstract protected BaseDispatchBus initDispatcher();

    abstract public String getBussinessType();

    private EventLogMapper getMapper(){
        EventLogMapper mapper = null;
        try {
            mapper = (EventLogMapper)applicationContext.getBean("eventLogMapper");
        }catch (Exception e){
            log.error("",e);
        }
        return mapper;
    }

    private EventLog buildEvent(IBaseEvent event){
        EventLog eventLog = new EventLog();
        BeanUtils.copyProperties(event, eventLog);
        eventLog.setDataClassType(event.getDataType().getName());
        eventLog.setData(event.getData());
        eventLog.setCreateTime(new Date());
        eventLog.setEventStatus(0);
        eventLog.setEventType(event.getType().name());
        eventLog.setBussinessType(getBussinessType());
        eventLog.setId(event.getEventId());
        if(event.getTraceId()!=null) {
            eventLog.setTraceId(String.valueOf(event.getTraceId()));
        }
        return eventLog;
    }

    @Override
    public void handler(final IBaseEvent baseEvent) {
        EventLogMapper mapper = getMapper();
        if(mapper == null){
            return;
        }
        if(baseEvent.getEventId()!=null){
            if(baseEvent.getRetry()>9){
                EventLog eventLog =new EventLog();
                eventLog.setId(baseEvent.getEventId());
                eventLog.setEventStatus(4);
                mapper.updateByPrimaryKeySelective(eventLog);
                return;
            }
        }
        EventLog eventLog = buildEvent(baseEvent);
        IBaseEvent event;
        if (baseEvent instanceof IDeviceEvent) {
            IDeviceEvent ie = (IDeviceEvent)baseEvent;
            eventLog.setDuid(ie.getDuid());
            event = new DeviceEvent();
            DeviceEvent sourceEvent = (DeviceEvent)baseEvent;
            BeanUtils.copyProperties(sourceEvent, event);
        }
        else if (baseEvent instanceof ITaskEvent) {
            if(baseEvent.getDataType().getSimpleName().equals("Subtask")) {
                Subtask st = JSONObject.parseObject(baseEvent.getData(), Subtask.class);
                eventLog.setSubTaskId(st.getId());
                eventLog.setTaskId(st.getTaskId().intValue());
            }
            TaskEvent sourceEvent = (TaskEvent)baseEvent;
            event = new TaskEvent();
            BeanUtils.copyProperties(sourceEvent, event);
        }else{
            return ;
        }
        if(eventLog.getId() == null){
            mapper.insertSelective(eventLog);
            event.setEventId(eventLog.getId());
            if(event.getTraceId() == 0){
                event.setTraceId(event.getEventId());
                eventLog.setTraceId(String.valueOf(event.getEventId()));
            }
            if(event.getOriginId() == 0){
                event.setOriginId(event.getEventId());
                eventLog.setOriginId(event.getEventId());
            }
        }
        //DB
        //如果重试测试多，拒绝重试--9次
        try {
            if (event instanceof IDeviceEvent) {
                deviceEventHandler((IDeviceEvent) event);
            }
            if (event instanceof ITaskEvent) {
                taskHandler((ITaskEvent) event);
            }
            eventLog.setEventStatus(1);
        }catch (Exception e){
            log.error("handler error",e);
            //DB
            int len = e.getMessage().length();
            eventLog.setException(e.getMessage().substring(0, len>199?199:len));
            eventLog.setEventStatus(2);
        }
        mapper.updateByPrimaryKeySelective(eventLog);
    }

    public void baseInit(){
        registerToBus();
    }

    protected void registerToBus(){
        dispatcher.onBus(this);
    }

    protected void emitEvent(IBaseEvent event){
        dispatcher.dispatchEvent(event);
    }

//    protected BaseDispatchBus getDispatcher() {
//        return dispatcher;
//    }

    protected void retry(IBaseEvent event){
        event.setRetry(event.getRetry()+1);
        sleep(200L);
        this.handler(event);
    }

    protected void retry(IBaseEvent event,long time){
        event.setRetry(event.getRetry()+1);
        sleep(time);
        this.handler(event);
    }

    protected void sleep(Long time){
        try {
            Thread.sleep(time);
        }catch (InterruptedException e){
            log.error("sleep error",e);
        }
    }

    protected void setDispatcher(BaseDispatchBus dispatcher) {
        this.dispatcher = dispatcher;
    }


    protected void sendTaskEvent(TaskDTO task, TaskEventType eventType, IBaseEvent event){
        TaskEvent taskEvent = new TaskEvent();
        taskEvent.setTaskId(task.getId());
        taskEvent.setEventType(eventType);
        taskEvent.setData(JSONObject.toJSONString(task));
        taskEvent.setDataType(TaskDTO.class);
        taskEvent.setOriginId(event.getOriginId());
        taskEvent.setTraceId(event.getEventId());
        emitEvent(taskEvent);
    }

    protected void sendTaskDeviceEvent(TaskDevice taskDevice, DeviceEventType deviceEventType){
        DeviceEvent deviceEvent = new DeviceEvent();
        deviceEvent.setDuid(taskDevice.getDuid());
        deviceEvent.setData(JSONObject.toJSONString(taskDevice));
        deviceEvent.setDataType(TaskDevice.class);
        deviceEvent.setEventType(deviceEventType);
        emitEvent(deviceEvent);
    }

    protected void sendTaskDeviceEvent(TaskDevice taskDevice, DeviceEventType deviceEventType, IBaseEvent event){
        DeviceEvent deviceEvent = new DeviceEvent();
        deviceEvent.setDuid(taskDevice.getDuid());
        deviceEvent.setData(JSONObject.toJSONString(taskDevice));
        deviceEvent.setDataType(TaskDevice.class);
        deviceEvent.setEventType(deviceEventType);
        deviceEvent.setOriginId(event.getOriginId());
        deviceEvent.setTraceId(event.getEventId());
        emitEvent(deviceEvent);
    }



    protected void sendTaskEvent(TaskDTO task, TaskEventType eventType){
        TaskEvent taskEvent = new TaskEvent();
        taskEvent.setTaskId(task.getId());
        taskEvent.setEventType(eventType);
        taskEvent.setData(JSONObject.toJSONString(task));
        taskEvent.setDataType(TaskDTO.class);
        emitEvent(taskEvent);
    }

    protected void sendTaskEvent(Subtask task, TaskEventType eventType, IBaseEvent event){
        TaskEvent taskEvent = new TaskEvent();
        taskEvent.setTaskId(task.getId().intValue());
        taskEvent.setEventType(eventType);
        taskEvent.setData(JSONObject.toJSONString(task));
        taskEvent.setDataType(Subtask.class);
        taskEvent.setOriginId(event.getOriginId());
        taskEvent.setTraceId(event.getEventId());
        emitEvent(taskEvent);
    }

    protected void sendTaskEvent(Subtask task, TaskEventType eventType){
        TaskEvent taskEvent = new TaskEvent();
        taskEvent.setTaskId(task.getId().intValue());
        taskEvent.setEventType(eventType);
        taskEvent.setData(JSONObject.toJSONString(task));
        taskEvent.setDataType(Subtask.class);
        emitEvent(taskEvent);
    }

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
